#include <iostream>
#include <string>
#include <memory>
#include "../../third_part/Muduo/muduo/examples/protobuf/codec/codec.h"
#include "../../third_part/Muduo/muduo/examples/protobuf/codec/dispatcher.h"
#include "./request.pb.h"
#include "../../third_part/Muduo/build/release-install-cpp11/include/muduo/base/Logging.h"
#include "../../third_part/Muduo/build/release-install-cpp11/include/muduo/base/Mutex.h"
#include "../../third_part/Muduo/build/release-install-cpp11/include/muduo/net/EventLoopThread.h"
#include "../../third_part/Muduo/build/release-install-cpp11/include/muduo/base/CountDownLatch.h"
#include "../../third_part/Muduo/build/release-install-cpp11/include/muduo/net/TcpClient.h"
using namespace std;


class Client {
public :
    typedef shared_ptr<reqNs::TranslateResponse> _tran_resp_ptr;
    typedef shared_ptr<reqNs::CalculateResponse> _cal_resp_ptr;

    Client(const string& serverip, int serverport) 
        :_latch(1)
        ,_dispatcher(std::bind(&Client::OnUnkownMessage, this, placeholders::_1, placeholders::_2, placeholders::_3))
        ,_codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, placeholders::_1, placeholders::_2, placeholders::_3))
        ,_client(_loopthread.startLoop(), muduo::net::InetAddress(serverip, serverport), "cllient")
    {
        _dispatcher.registerMessageCallback<reqNs::TranslateResponse>(std::bind(&Client::OnTranslate, this, placeholders::_1, placeholders::_2, placeholders::_3));
        _dispatcher.registerMessageCallback<reqNs::CalculateResponse>(std::bind(&Client::OnCalculate, this, placeholders::_1, placeholders::_2, placeholders::_3));
        _client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec, placeholders::_1, placeholders::_2, placeholders::_3));
        _client.setConnectionCallback(std::bind(&Client::OnConnection, this, placeholders::_1));

    }

    void Translate(const string& word) {
        reqNs::TranslateRequest req;
        req.set_word(word);
        Send(&req);

    }
    void Calculate(int x, int y, string op) {
        reqNs::CalculateRequest req;
        req.set_x(x);
        req.set_y(y);
        req.set_op(op);
        Send(&req);
    }
public :
    void Connect() {
        // 连接服务器
        _client.connect();
        _latch.wait();
    }

    bool Send(const google::protobuf::Message* message) {
        if (_conn->connected()) {
            // 发送数据
            _codec.send(_conn, *message);
            return true;
        } else {
            return false;
        }
    }



private :
    void OnConnection(const muduo::net::TcpConnectionPtr& conn) {
        if (conn->connected()) {
            _latch.countDown(); // 唤醒追线程中的阻塞
            _conn = conn;  
        } else {
            // 连接关闭时的操作
            // conn->reset();
        }
    }
    // 未知请求的处理方法
    void OnUnkownMessage(const muduo::net::TcpConnectionPtr& conn, const MessagePtr& message, muduo::Timestamp) {
        conn->shutdown();

    }
    // 处理翻译的请求处理函数
    void OnTranslate(const muduo::net::TcpConnectionPtr& conn, const _tran_resp_ptr& message, muduo::Timestamp) {
        cout << "翻译结果: " << message->answer() << endl;

    }

    // 处理计算器的处理函数
    void OnCalculate(const muduo::net::TcpConnectionPtr& conn, const _cal_resp_ptr& message, muduo::Timestamp) {
        cout << "计算结果: " << message->result() << " 错误码: " << message->code() << endl;
    }
private :
    muduo::CountDownLatch _latch; // 实现同步的
    muduo::net::EventLoopThread _loopthread; // 异步处理线程
    muduo::net::TcpConnectionPtr _conn; // 客户端对应的连接
    muduo::net::TcpClient _client; // 服务器对象
    ProtobufDispatcher _dispatcher; // 请求分发器对象——要想其中注册请求处理函数的
    ProtobufCodec _codec; // protobuf协议处理对象——针对收到的请求数据进行protobuf处理
};


int main(int argc, char* args[]) {
    if (argc != 3) {
        cout << "Usage:\r\n" << args[0] << " server_ip server_port\b" << endl;
        return 0;
    }
    string serverip = args[1];
    int serverport = stoi(args[2]);
    Client client(serverip, serverport);
    client.Connect();

    int op = 0;
    cout << "请选择服务类型(1.翻译  2.计算器)# ";
    cin >> op;
    if (op == 1) {
        while (true) {
            cout << "Please Enter# ";
            string word;
            getline(cin, word);
            client.Translate(word);
        }
    } else if (op == 2) {
        int x = 0;
        int y = 0;
        string op;
        cout << "请输入第一个运算数: ";
        cin >> x;
        cout << "请输入运算符: ";
        cin >> op;
        cout << "请输入第二个运算数: ";
        cin >> y;
        client.Calculate(x, y, op);
    } else {

    }
    
    return 0;
}